package by.apiskun.curex.dataaccess.impl;

import java.util.List;

import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.Validate;
import org.springframework.stereotype.Repository;

import by.apiskun.curex.dataaccess.WalletDAO;
import by.apiskun.curex.datamodel.Money_;
import by.apiskun.curex.datamodel.User_;
import by.apiskun.curex.datamodel.Wallet;
import by.apiskun.curex.datamodel.Wallet_;

@Repository
public class WalletDAOImpl extends AbstractDAOImpl<Long, Wallet> implements
		WalletDAO {
	protected WalletDAOImpl() {
		super(Wallet.class);
	}

	@Override
	public Wallet getById(Long id) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Wallet> criteria = cBuilder.createQuery(Wallet.class);
		Root<Wallet> root = criteria.from(Wallet.class);

		criteria.select(root);
		root.fetch(Wallet_.money);
		root.fetch(Wallet_.user);
		criteria.distinct(true);

		TypedQuery<Wallet> query = getEm().createQuery(criteria);
		List<Wallet> results = query.getResultList();
		if (!results.isEmpty()) {
			return results.get(0);
		} else {
			return null;
		}

	}

	@Override
	public Long getCount() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Long> criteria = cBuilder.createQuery(Long.class);
		Root<Wallet> root = criteria.from(Wallet.class);

		criteria.select(cBuilder.count(root));

		TypedQuery<Long> query = getEm().createQuery(criteria);
		return query.getSingleResult();
	}

	@Override
	public List<Wallet> getAllWalletsByMoneyName(String name) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Wallet> root = cBuilder.createQuery(Wallet.class);
		Root<Wallet> criteria = root.from(Wallet.class);

		root.select(criteria);
		criteria.fetch(Wallet_.money);
		root.distinct(true);
		root.where(cBuilder.equal(
				criteria.get(Wallet_.money).get(Money_.moneyName), name));

		TypedQuery<Wallet> query = getEm().createQuery(root);
		List<Wallet> results = query.getResultList();
		return results;
	}

	@Override
	public List<Wallet> getAllWalletsByAmount(Double amount) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Wallet> root = cBuilder.createQuery(Wallet.class);
		Root<Wallet> criteria = root.from(Wallet.class);

		root.select(criteria);
		root.where(cBuilder.equal(criteria.get(Wallet_.walletAmount), amount));

		TypedQuery<Wallet> query = getEm().createQuery(root);
		List<Wallet> results = query.getResultList();
		return results;
	}

	@Override
	public List<Wallet> getAllWalletsByAmountRange(Double minAmount,
			Double maxAmount) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Wallet> root = cBuilder.createQuery(Wallet.class);
		Root<Wallet> criteria = root.from(Wallet.class);

		root.select(criteria);
		root.where(cBuilder.greaterThan(criteria.get(Wallet_.walletAmount),
				minAmount));
		root.where(cBuilder.lessThan(criteria.get(Wallet_.walletAmount),
				maxAmount));

		TypedQuery<Wallet> query = getEm().createQuery(root);
		List<Wallet> results = query.getResultList();
		return results;
	}

	@Override
	public void updateWalletAmount(Long id, Double newAmount) {
		String q = String.format(
				"update from %s p set p.walletAmount=:newParam where p.id=:id",
				Wallet.class.getSimpleName());
		Query updateQuery = getEm().createQuery(q);
		updateQuery.setParameter("newParam", newAmount);
		updateQuery.setParameter("id", id);
		int updatedRowsCount = updateQuery.executeUpdate();
		Validate.isTrue(updatedRowsCount == 1, "One row should be updated");
	}

	@Override
	public List<Wallet> getAllWalets() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Wallet> criteria = cBuilder.createQuery(Wallet.class);
		Root<Wallet> root = criteria.from(Wallet.class);

		criteria.select(root);

		TypedQuery<Wallet> query = getEm().createQuery(criteria);
		List<Wallet> results = query.getResultList();
		return results;
	}

	@Override
	public List<Wallet> getAllWalletsByUserLogin(String login) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Wallet> root = cBuilder.createQuery(Wallet.class);
		Root<Wallet> criteria = root.from(Wallet.class);

		root.select(criteria);
		criteria.fetch(Wallet_.user);
		root.distinct(true);
		root.where(cBuilder.equal(criteria.get(Wallet_.user).get(User_.login),
				login));

		TypedQuery<Wallet> query = getEm().createQuery(root);
		List<Wallet> results = query.getResultList();
		return results;
	}
}
